//
//  SpringBoardViewButton.m
//  SpringBoardView
//
//  Created by Kishikawa Katsumi on 10/01/24.
//  Copyright 2010 Kishikawa Katsumi. All rights reserved.
//

#import "SpringBoardViewButton.h"
#import "SpringBoardViewDeleteButton.h"
#import "SpringBoardViewController.h"
#import <QuartzCore/QuartzCore.h>

#define ANIMATION_DURATION_SECONDS 0.15

NSString *const SpringBoardViewBeginEditingNotification = @"SpringBoardViewBeginEditingNotification";
NSString *const SpringBoardViewButtonLayoutChangingNotification = @"SpringBoardViewButtonLayoutChangingNotification";
NSString *const SpringBoardViewButtonLayoutChangedNotification = @"SpringBoardViewButtonLayoutChangedNotification";

@interface UIImage(ApplicationIconPrivate)
- (UIImage *)_applicationIconWithSize:(struct CGSize)fp8 destinationFrame:(struct CGRect)fp16 shadowImage:(id)fp32 overlayImage:(id)fp36 outlineImage:(id)fp40 maskImage:(id)fp44;
- (id)_applicationIconImagePrecomposed:(BOOL)fp8;
- (id)_smallApplicationIconImagePrecomposed:(BOOL)fp8;
@end

@interface SpringBoardViewButton(Private)
- (void)setupWithTitle:(NSString *)aTitle iconImage:(UIImage *)image;
- (void)beginButtonLayoutEditing:(UITouch *)touch withEvent:(UIEvent *)event;
- (void)continueButtonLayoutEditing:(UITouch *)touch withEvent:(UIEvent *)event;
- (void)endButtonLayoutEditing:(UITouch *)touch withEvent:(UIEvent *)event;
- (void)doWiggle;
- (void)endWiggle;
- (CGRect)buttonPositionFromCenter:(CGPoint)center;
@end

@implementation SpringBoardViewButton

@synthesize controller;
@synthesize title;
@synthesize iconImage;
@synthesize isDragging;
@synthesize isEditing;

- (id) init {
	if (self = [super init]) {
		[self setupWithTitle:@"Untitled" iconImage:[[UIImage imageNamed:@"icon_default.png"] _applicationIconImagePrecomposed:NO]];
	}
	return self;
}

- (id)initWithTitle:(NSString *)aTitle iconImage:(UIImage *)image {
	if (self = [super init]) {
		[self setupWithTitle:aTitle iconImage:image];
	}
	return self;
}

- (void)setupWithTitle:(NSString *)aTitle iconImage:(UIImage *)image {
	self.title = aTitle;
	self.iconImage = image;
	
	[self setImage:iconImage forState:UIControlStateNormal];
	
	[self setContentVerticalAlignment:UIControlContentVerticalAlignmentTop];
	[self setContentHorizontalAlignment:UIControlContentHorizontalAlignmentCenter];
	
	deleteButton = [[SpringBoardViewDeleteButton button] retain];
	[deleteButton addTarget:self action:@selector(deleteButtonPushed:) forControlEvents:UIControlEventTouchUpInside];
	[deleteButton setAlpha:0.0f];
	[self addSubview:deleteButton];
}

- (void)dealloc {
    [title release];
    [iconImage release];
	[deleteButton release];
	[super dealloc];
}

#pragma mark Drawing and Layout

- (void)drawRect:(CGRect)rect {
	[super drawRect:rect];
	[[UIColor whiteColor] set];
	[title drawInRect:CGRectMake(0.0f, 61.0f, 74.0f, 17.0f) withFont:[UIFont boldSystemFontOfSize:11.0f] 
		lineBreakMode:UILineBreakModeMiddleTruncation alignment:UITextAlignmentCenter];
}

- (void)layoutSubviews {
	[super layoutSubviews];
	[self bringSubviewToFront:deleteButton];
}

- (void)setFrame:(CGRect)aFrame {
	[super setFrame:aFrame];
	fromRect = aFrame;
}

- (void)setEditingWithNumber:(NSNumber *)editing {
	isEditing = [editing boolValue];
	if (isEditing) {
		[self doWiggle];
	} else {
		[self endWiggle];
	}
}

- (void)setEditing:(BOOL)editing {
	isEditing = editing;
	if (isEditing) {
		[self doWiggle];
	} else {
		[self endWiggle];
	}
}

#pragma mark Touch Event Handling

- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
    LOG_CURRENT_METHOD;
	if (isEditing) {
		return NO;
	}
	[self performSelector:@selector(beginButtonLayoutEditing:withEvent:) withObject:nil afterDelay:1.0f];
	return [super beginTrackingWithTouch:touch withEvent:event];
}

- (void)cancelTrackingWithEvent:(UIEvent *)event {
    LOG_CURRENT_METHOD;
	[NSObject cancelPreviousPerformRequestsWithTarget:self];
	return [super cancelTrackingWithEvent:event];
}

- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
    LOG_CURRENT_METHOD;
	CGPoint locationInView = [touch locationInView:[self superview]];
	if (!CGRectContainsPoint(self.frame, locationInView)) {
		[NSObject cancelPreviousPerformRequestsWithTarget:self];
		[self setHighlighted:NO];
		return NO;
	}
	return [super continueTrackingWithTouch:touch withEvent:event];
}

- (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
    LOG_CURRENT_METHOD;
	CGPoint locationInView = [touch locationInView:[self superview]];
	if (!CGRectContainsPoint(self.frame, locationInView)) {
		[self setHighlighted:NO];
	}
	[NSObject cancelPreviousPerformRequestsWithTarget:self];
	[super endTrackingWithTouch:touch withEvent:event];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    LOG_CURRENT_METHOD;
	if (isEditing) {
		UITouch *touch = [touches anyObject];
		[self beginButtonLayoutEditing:touch withEvent:event];
		return;
	}
	[super touchesBegan:touches withEvent:event];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
	if (isEditing) {	
		UITouch *touch = [touches anyObject];
		CGPoint location = [touch locationInView:[self superview]];
		self.center = location;

		CGRect currentRect = [self buttonPositionFromCenter:location];
		if (!CGRectEqualToRect(currentRect, fromRect)) {
			[self continueButtonLayoutEditing:touch withEvent:event];
		}
        
		return;
	}
	[super touchesMoved:touches withEvent:event];
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
	LOG_CURRENT_METHOD;
	if (isEditing) {
		[UIView beginAnimations:nil context:NULL];
		[UIView setAnimationDuration:ANIMATION_DURATION_SECONDS];
		self.transform = CGAffineTransformIdentity;
		[UIView commitAnimations];
		
		[self doWiggle];
		
		UITouch *touch = [touches anyObject];
		[self endButtonLayoutEditing:touch withEvent:event];
		
		return;
	}
	[NSObject cancelPreviousPerformRequestsWithTarget:self];
	[self setHighlighted:NO];
	[super touchesCancelled:touches withEvent:event];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
	LOG_CURRENT_METHOD;
	if (isEditing) {
		[UIView beginAnimations:nil context:NULL];
		[UIView setAnimationDuration:ANIMATION_DURATION_SECONDS];
		self.transform = CGAffineTransformIdentity;
		[UIView commitAnimations];
		
		[self doWiggle];
		
		UITouch *touch = [touches anyObject];
		[self endButtonLayoutEditing:touch withEvent:event];
		
		return;
	}
	[super touchesEnded:touches withEvent:event];
}

#pragma mark State Control

- (void)beginButtonLayoutEditing:(UITouch *)touch withEvent:(UIEvent *)event {
	if (!isEditing) {
		[[NSNotificationCenter defaultCenter] postNotificationName:SpringBoardViewBeginEditingNotification object:self];
		self.isEditing = YES;
	}
	
	[self endWiggle];
	isDragging = YES;
	self.highlighted = NO;
	
	[[self superview] bringSubviewToFront:self];
	
	[UIView beginAnimations:nil context:NULL];
	[UIView setAnimationDuration:ANIMATION_DURATION_SECONDS];
	CGAffineTransform transform = CGAffineTransformMakeScale(1.25f, 1.25f);
	self.transform = transform;
	self.alpha = 0.5f;
	[UIView commitAnimations];
}

- (void)continueButtonLayoutEditing:(UITouch *)touch withEvent:(UIEvent *)event {
	CGRect toRect = [self buttonPositionFromCenter:self.center];
    
    NSUInteger fromIndex = [SpringBoardViewController buttonIndexFromCGRect:fromRect];
	CGRect lastAvailableRect = [controller lastItemPosition];
	NSUInteger lastAvailableIndex = [SpringBoardViewController buttonIndexFromCGRect:lastAvailableRect];
	
	if (fromIndex > lastAvailableIndex) {
		fromRect = lastAvailableRect;
	}
    
	NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
							  [NSValue valueWithCGRect:fromRect], @"fromRect",
							  [NSValue valueWithCGRect:toRect], @"toRect", nil];
    [[NSNotificationCenter defaultCenter] postNotificationName:SpringBoardViewButtonLayoutChangingNotification object:self userInfo:userInfo];
    
    fromRect = toRect;
}

- (void)endButtonLayoutEditing:(UITouch *)touch withEvent:(UIEvent *)event {
	[UIView beginAnimations:nil context:NULL];
	[UIView setAnimationDuration:ANIMATION_DURATION_SECONDS];
	[self setAlpha:1.0f];
	[self setHighlighted:NO];
	[UIView commitAnimations];
	
	isDragging = NO;
	
    CGRect toRect = [self buttonPositionFromCenter:self.center];
    
    NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
    						  [NSValue valueWithCGRect:fromRect], @"fromRect",
    						  [NSValue valueWithCGRect:toRect], @"toRect", nil];
    [[NSNotificationCenter defaultCenter] postNotificationName:SpringBoardViewButtonLayoutChangedNotification object:self userInfo:userInfo];
}

- (void)doWiggle {
	isWiggling = YES;
	
	[UIView beginAnimations:nil context:NULL];
	[UIView setAnimationDuration:ANIMATION_DURATION_SECONDS];
	[deleteButton setAlpha:1.0f];
	[UIView commitAnimations];
	
	CALayer *layer = [self layer];
	
    CABasicAnimation *rotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    rotation.fromValue = [NSNumber numberWithFloat:M_PI * -1.0f / 180.0f];
    rotation.toValue = [NSNumber numberWithFloat:M_PI * 1.0f / 180.0f];
	rotation.duration = 0.12;
    rotation.removedOnCompletion = NO;
	rotation.autoreverses = YES;
    rotation.fillMode = kCAFillModeBoth; 
    rotation.repeatCount = 1e100f;
    rotation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
	
	[layer addAnimation:rotation forKey:@"wiggle"];
}

- (void)endWiggle {
	isWiggling = NO;
	
	[UIView beginAnimations:nil context:NULL];
	[UIView setAnimationDuration:ANIMATION_DURATION_SECONDS];
	[deleteButton setAlpha:0.0f];
	[UIView commitAnimations];
	
	self.transform = CGAffineTransformIdentity;
	[[self layer] removeAllAnimations];
}

#pragma mark Utility Method

- (CGRect)buttonPositionFromCenter:(CGPoint)center {
	NSUInteger x = 0;
	NSUInteger y = 0;
	if (center.x > 240.0f) {
		x = 3;
	} else if (center.x > 160.0f) {
		x = 2;
	} else if (center.x > 80.0f) {
		x = 1;
	} else {
		x = 0;
	}
	if (center.y > 260.0f) {
		y = 3;
	} else if (center.y > 174.0f) {
		y = 2;
	} else if (center.y > 87.0f) {
		y = 1;
	} else {
		y = 0;
	}
	CGRect frame;
	frame.origin.x = 4.0f + 80.0f * x;
	frame.origin.y = 8.0f + 86.0f * y;
	frame.size.width = 74.0f;
	frame.size.height = 75.0f;
	return frame;
}

#pragma mark Delete Action

- (void)deleteButtonPushed:(id)sender {
}

@end
